﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using SE.Halligang.CsXmpToolkit;
using SE.Halligang.CsXmpToolkit.Schemas;
using System.Threading;

namespace Antlers
{
	internal class Thumbnail
	{
		private String m_filePath;
		private Image m_thumb;
		private HashSet<String> m_keywords;
		private DateTime m_keywordsLastUpdated = DateTime.Now;
		private DateTime m_imageLastUpdated = DateTime.Now;
		private object m_keywordUpdateLock = new object();
		private object m_imageUpdateLock = new object();
		
		internal delegate void GetKeywordsCallback(Thumbnail sender, IEnumerable<String> keywords);
		
		internal Thumbnail(String filePath)
		{
			m_filePath = filePath;
		}

		internal String FilePath
		{
			get { return m_filePath; }
		}

		public Image Image
		{
			get 
			{
				EnsureUpdateImage();
				return m_thumb; 
			}
		}

		public HashSet<String> Keywords
		{
			get
			{
				EnsureUpdateKeywords();
				return m_keywords;
			}
		}

		private bool IsKeywordListUpToDate
		{
			get
			{
				return m_keywordsLastUpdated.Equals(File.GetLastWriteTime(FilePath));
			}
		}

		private bool IsImageUpToDate
		{
			get
			{
				return m_imageLastUpdated.Equals(File.GetLastWriteTime(FilePath));
			}
		}

		internal void AddKeyword(string newKeyword)
		{
			EnsureUpdateKeywords();
			Lock.RunSafe(m_keywordUpdateLock, () => !m_keywords.Contains(newKeyword), () =>
			{
				m_keywords.Add(newKeyword);

				using(Xmp xmp = Xmp.FromFile(m_filePath, XmpFileMode.ReadWrite))
				{
					DublinCore core = new DublinCore(xmp);
					core.Subject.Add(newKeyword);
					xmp.Save();
				}
			});
		}

		internal void RemoveKeyword(string keyword)
		{
			EnsureUpdateKeywords();
			Lock.RunSafe(m_keywordUpdateLock, () => m_keywords.Contains(keyword), () =>
			{
				m_keywords.Remove(keyword);

				using (Xmp xmp = Xmp.FromFile(m_filePath, XmpFileMode.ReadWrite))
				{
					DublinCore core = new DublinCore(xmp);
					core.Subject.RemoveAt(core.Subject.IndexOf(keyword));
					xmp.Save();
				}
			});
		}

		internal bool ContainsAnyKeyword(IEnumerable<string> keywords)
		{
			EnsureUpdateKeywords();

			List<string> matchKeywords = null;
			List<string> keywordsClone = null;

			Lock.RunSafe(m_keywordUpdateLock, () =>
			{
				matchKeywords = new List<string>(keywords);
				keywordsClone = new List<string>(m_keywords);
			});

			foreach (String keyword in matchKeywords)
			{
				if (keywordsClone.Contains(keyword))
				{
					return true;
				}
			}

			return false;
		}

		internal void EnsureUpdateImage()
		{
			Lock.RunSafe(
				m_imageUpdateLock,
				delegate()
				{
					return !IsImageUpToDate;
				},
				delegate()
				{
					using (FileStream stream = new FileStream(m_filePath, FileMode.Open, FileAccess.Read))
					{
						using (Image image = Image.FromStream(stream, true, false))
						{
							m_thumb = image.GetThumbnailImage(0, 0, Abort, IntPtr.Zero);
						}
					}

					m_imageLastUpdated = File.GetLastWriteTime(FilePath);
				}
			);
		}

		internal void EnsureUpdateKeywords()
		{
			Lock.RunSafe(
				m_keywordUpdateLock,
				delegate()
				{
					return !IsKeywordListUpToDate;
				},
				delegate()
				{
					if (m_keywords == null)
					{
						m_keywords = new HashSet<string>();
					}

					m_keywords.Clear();

					using (Xmp xmp = Xmp.FromFile(m_filePath, XmpFileMode.ReadOnly))
					{
						DublinCore core = new DublinCore(xmp);

						foreach (String keyword in core.Subject)
						{
							m_keywords.Add(keyword);
						}
					}

					m_keywordsLastUpdated = File.GetLastWriteTime(FilePath);
				});
		}

		private bool Abort()
		{
			return false;
		}
	}
}
